package manager.match;


import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Random;

import org.apache.log4j.Logger;
import org.springframework.beans.BeansException;


import com.fasterxml.jackson.core.JsonProcessingException;

import advice.ExceptionManager;
import io.netty.channel.ChannelFuture;
import manager.connection.ClientConnectionManager;
import manager.connection.type.ConnectionInfo;
import manager.match.basic.state.EnrollPlayerState;

import manager.match.basic.type.EnrollPlayerInfo;

import manager.player.PlayerManager;
import manager.player.basic.state.GeneralPlayerState;
import manager.player.basic.type.BasicPlayerInfo;
import net.socket.server.protocal.PROTOID;
import net.socket.server.request.ChallengePlayerRequest;
import net.socket.server.request.EnrollRequest;
import net.socket.server.request.GetEnrollInfoRequest;
import net.socket.server.request.GetRandomEnrollListRequest;
import net.socket.server.request.IRequest;
import net.socket.server.request.QuitEnrollRequest;
import net.socket.server.response.ChallengePlayerResponse;
import net.socket.server.response.EnrollResponse;
import net.socket.server.response.GetEnrollInfoResponse;
import net.socket.server.response.GetRandomMEnrollListResponse;
import net.socket.server.response.QuitEnrollResponse;
import observable.subject.MessageCenter;
import util.MyFactory;
import util.ResultTypeList;

public class EnrollmentManager {
	// the id of the song and a list of player enrolled
	private  Map<String, List<EnrollPlayerInfo>> enrollList = new HashMap<String, List<EnrollPlayerInfo>>();
	private static final long WAIT_FOR_PLAYER_RECONNECTED_OVERTIME = 30 * 1000;//30s
	public static final int IS_CHALLENGER=1;//挑战者
	public static final int NOT_CHALLENGER=0;//不是挑战者
	private static Logger logger = Logger.getLogger(ExceptionManager.class);
	
	// reference of other class
	private MatchManager mm = null;
	private ClientConnectionManager ccm = null;
	private PlayerManager pm = null;
	private Random random = new Random();
	public void init(){
		MessageCenter messageCenter = MyFactory.context.getBean("MessageCenter", MessageCenter.class);
		messageCenter.register(PROTOID.MATCH_ENROLL_REQ, (int cmd, IRequest req)->handleEnroll(cmd, req));
		messageCenter.register(PROTOID.GET_ENROLL_INFO_REQ , (int cmd, IRequest req)->getEnrollInfo(cmd, req));
		messageCenter.register(PROTOID.MATCH_QUIT_ENROLL_REQ, (int cmd, IRequest request)->handleQuitEnroll(cmd, request));
		messageCenter.register(PROTOID.HEART_BEAT_ONE_TENTH_SECOND, (int cmd, IRequest request)->onTimer(cmd, request));
		messageCenter.register(PROTOID.GET_RANDOM_ENROLL_LIST_REQ, (int cmd, IRequest request)->handleGetRandomEnrollList(cmd, request));
		messageCenter.register(PROTOID.CHALLENGE_PLAYER_REQ, (int cmd, IRequest request)->handleChallengePlayer(cmd, request));
		
		
		// init manager reference
		mm = MyFactory.context.getBean("MatchManager", MatchManager.class);
		ccm = MyFactory.context.getBean("ClientConnectionManager", ClientConnectionManager.class);
		pm = MyFactory.context.getBean("PlayerManager", PlayerManager.class);
	}
	//处理获取自己的报名信息
	private int getEnrollInfo(int cmd, IRequest request) throws IllegalArgumentException, IllegalAccessException, JsonProcessingException {
		//System.out.println("getEnrollInfo---------------");
		GetEnrollInfoRequest req = (GetEnrollInfoRequest)request ;
		GetEnrollInfoResponse rsp = new GetEnrollInfoResponse();
		ChannelFuture cf = null;
		if("".equals(req.getOpenId())) {
			rsp.setResult(ResultTypeList.NULL_ARGS);
			ccm.sendResponse(PROTOID.GET_ENROLL_INFO_RSP, req.getMachineId(), rsp);
			return ResultTypeList.NULL_ARGS;
		}
		
		 EnrollPlayerInfo enrollPlayerInfo = getEnrollPlayerInfoByOpenId(req.getOpenId());
		if(null == enrollPlayerInfo) {
			rsp.setResult(ResultTypeList.SUCCESS);
			//System.out.println("getEnrollInfo--------the enrollPlayerInfo is NULL");
			logger.info("getEnrollInfo--------the enrollPlayerInfo is NULL");
		}else {
			rsp.setResult(ResultTypeList.SUCCESS);
		}
		rsp.setEnrollPlayerInfo(enrollPlayerInfo);
		cf = ccm.sendResponse(PROTOID.GET_ENROLL_INFO_RSP, req.getMachineId(), rsp);
		if(cf == null) {
			return ResultTypeList.NO_CONNECTION_FOUND;
		}else {
			return ResultTypeList.SUCCESS;
		}
	}

	public int onTimer(int cmd, IRequest request) throws IllegalArgumentException, IllegalAccessException, JsonProcessingException{
		// check for match overtime
		checkDisconnection();
		return cmd;
	}
	
	public void checkDisconnection() throws IllegalArgumentException, IllegalAccessException, JsonProcessingException{
		for (List<EnrollPlayerInfo> list : enrollList.values()) {
			if(list != null){
				Iterator<EnrollPlayerInfo> it = list.iterator();
				EnrollPlayerInfo player = null;
				while(it.hasNext()){
				    player = it.next();
				    if(ccm.getConnectionInfo(player.getMachineId()) == null){
						if(player.isConnected()){
							player.setConnected(false);
						}else{
							long duration = System.currentTimeMillis() - player.getStartTime();
							if(duration > WAIT_FOR_PLAYER_RECONNECTED_OVERTIME){
								//System.out.println("remove player for disconnection from too long > 30:" + player.getPlayerInfo().getOpenId());
								logger.info("remove player for disconnection from too long > 30:" + player.getPlayerInfo().getOpenId());
								/*QuitEnrollResponse rsp = new QuitEnrollResponse();
								rsp.setSongId(player.getSongId());
								rsp.setResult(ResultTypeList.SUCCESS);
								ccm.sendResponse(PROTOID.MATCH_QUIT_ENROLL_RSP, player.getMachineId(), rsp);*/
								
								it.remove();
								pm.setPlayerState(player.getPlayerInfo().getOpenId(), GeneralPlayerState.CREATED);
								pm.setPlayerSongId(player.getPlayerInfo().getOpenId(), "");
								//pm.removePlayerFromList(player.getPlayerInfo().getOpenId());
								}
						}
					}else {
						if(!player.isConnected()){
							player.setConnected(true);
						}
					}
				}
			}
		}
	}
	
	//处理报名请求
	public int handleEnroll(int cmd, IRequest request) throws BeansException, SQLException, IllegalArgumentException, IllegalAccessException, JsonProcessingException{
		EnrollRequest req = (EnrollRequest) request;
		EnrollResponse rsp = new EnrollResponse();
		if("".equals(req.getSongId())) {
			rsp.setResult(ResultTypeList.NULL_ARGS);
			ccm.sendResponse(PROTOID.MATCH_ENROLL_RSP, req.getMachineId(), rsp);
			return ResultTypeList.NULL_ARGS;		
		}
		// create player info
		String openId = ccm.getOpenIdByMachine(req.getMachineId());
		EnrollPlayerInfo player = createEnrollPlayerInfo(req.getSongId(), req.getMachineId(), NOT_CHALLENGER, openId);//1表示挑战者

		int playerState = pm.getGeneralPlayerInfo(openId).getState();
		if(playerState == GeneralPlayerState.ENROLLED || playerState == GeneralPlayerState.IN_MATCH){
			//System.out.println("handleEnroll:"+playerState);
			logger.info("handleEnroll:"+playerState);
			rsp.setSongId(req.getSongId());
			rsp.setResult(ResultTypeList.PLAYER_ENROLLED);
			ccm.sendResponse(PROTOID.MATCH_ENROLL_RSP, req.getMachineId(), rsp);
			return ResultTypeList.PLAYER_ENROLLED;
		}
		logger.info("get enroll request for song: " + req.getSongId() + " by " + req.getMachineId());
		//System.out.println("get enroll request for song: " + req.getSongId() + " by " + req.getMachineId());
		if(addPlayerToEnrollList(req.getSongId(), player)){
			rsp.setResult(ResultTypeList.SUCCESS);
		}
		else{
			rsp.setResult(ResultTypeList.CANT_JION_ENROLLEND_LIST);
		}
		rsp.setSongId(req.getSongId());
		ccm.sendResponse(PROTOID.MATCH_ENROLL_RSP, req.getMachineId(), rsp);
		
		EnrollPlayerInfo opponent = getMatchedPlayer(req.getSongId(),openId);//获取匹配对手
		rsp.setSongId(req.getSongId());
		if(opponent == null){
			//System.out.println("found no match");
			logger.info("found no match");
		}else{
			//System.out.println("found a match");
			logger.info("found a match");
			List<EnrollPlayerInfo> matchPlayerList = new ArrayList<EnrollPlayerInfo>();
			matchPlayerList.add(player);
			matchPlayerList.add(opponent);
			//创建一场比赛
			mm.createMatch(req.getSongId(), matchPlayerList);
		}
		return ResultTypeList.SUCCESS;
		
	}
	
	//处理退出报名的请求
	public int handleQuitEnroll(int cmd, IRequest request) throws IllegalArgumentException, IllegalAccessException, JsonProcessingException{
		QuitEnrollRequest req = (QuitEnrollRequest) request;
		QuitEnrollResponse rsp = new QuitEnrollResponse();
		if("".equals(req.getSongId())) {
			rsp.setResult(ResultTypeList.NULL_ARGS);
			ccm.sendResponse(PROTOID.MATCH_QUIT_ENROLL_RSP, req.getMachineId(), rsp);
			return ResultTypeList.NULL_ARGS;
			
		}
		rsp.setSongId(req.getSongId());
		String openId = ccm.getOpenIdByMachine(req.getMachineId());
		removePlayerFromEnrollList(req.getSongId(), openId,req.getMachineId());
		return ResultTypeList.SUCCESS;
	}
	
	private List<EnrollPlayerInfo> getList(String songId){
		return enrollList.get(songId);
	}
	
	private void addList(String songId, List<EnrollPlayerInfo> list){
			enrollList.put(songId, list);
		
	}
	
	//将玩家加入匹配列表
	public boolean addPlayerToEnrollList(String songId, EnrollPlayerInfo playerInfo){
		List<EnrollPlayerInfo> list = getList(songId);
		if(list == null){
			list = new ArrayList<EnrollPlayerInfo>();
			addList(songId, list);
			//System.out.println("create a new list for song id " + songId);
		}
		playerInfo.setState(EnrollPlayerState.WAITING);
		pm.setPlayerState(playerInfo.getPlayerInfo().getOpenId(), GeneralPlayerState.ENROLLED);
		pm.setPlayerSongId(playerInfo.getPlayerInfo().getOpenId(), songId);
		
		return list.add(playerInfo);
	}
	
	//将玩家从匹配列表中删除,并且通知玩家离开匹配列表
	public boolean removePlayerFromEnrollList(String songId, String openId, int mid) throws IllegalArgumentException, IllegalAccessException, JsonProcessingException{
		List<EnrollPlayerInfo> list = getList(songId);
		boolean result = false;
		if(list != null){
				Iterator<EnrollPlayerInfo> it = list.iterator();
				EnrollPlayerInfo player = null;
				while(it.hasNext()){
				    player = it.next();
				    if(player.getPlayerInfo().getOpenId().equals(openId)){
				        it.remove();
						pm.setPlayerState(openId, GeneralPlayerState.CREATED);
						pm.setPlayerSongId(openId, "");
						result = true;
						
						QuitEnrollResponse rsp = new QuitEnrollResponse();
						rsp.setSongId(player.getSongId());
						rsp.setResult(ResultTypeList.SUCCESS);
						ccm.sendResponse(PROTOID.MATCH_QUIT_ENROLL_RSP, player.getMachineId(), rsp);
						System.out.println("removePlayerFromEnrollList of :"+openId);
						logger.info("removePlayerFromEnrollList of :"+openId);
						return result;
				   
				}
			}
			
		}
		//System.out.println("NO found Player from the EnrollList");
		logger.info("NO found Player from the EnrollList");
		QuitEnrollResponse rsp = new QuitEnrollResponse();
		rsp.setSongId(songId);
		rsp.setResult(ResultTypeList.SUCCESS);
		ccm.sendResponse(PROTOID.MATCH_QUIT_ENROLL_RSP, mid, rsp);
		//System.out.println("removePlayerFromEnrollList of :"+openId);
		return result;
	}
	
	//获取匹配对手，这时候不应该从列表中删除，只有创建比赛的时候才可以
	public EnrollPlayerInfo getMatchedPlayer(String songId,String openId){
		List<EnrollPlayerInfo> list = getList(songId);
		EnrollPlayerInfo player = null;
		if(list != null){
			
				for (EnrollPlayerInfo enrollPlayerInfo : list) {
					if(enrollPlayerInfo.getState() == EnrollPlayerState.WAITING
							&&!enrollPlayerInfo.getPlayerInfo().getOpenId().equals(openId)){
						System.out.println("find player " + enrollPlayerInfo.getMachineId() 
						+ " state is: " + enrollPlayerInfo.getState());
						player = enrollPlayerInfo;
						//list.remove(enrollPlayerInfo);
						break;
					}
				}
			
		}
		else{
			//System.out.println("no list for songid" + songId);
		}
		return player;
	}
	
	//根据openid获取匹配选手
	public EnrollPlayerInfo getMatchedPlayerByOpenId(String openId,String songId) {
		
		List<EnrollPlayerInfo> list = getList(songId);
		EnrollPlayerInfo player = null;
		EnrollPlayerInfo returnplayer = null;
		if(list != null){
			Iterator<EnrollPlayerInfo> it = list.iterator();
			while(it.hasNext()){
			    player = it.next();
			    //System.out.println("palyer: "+ player);
			    if(player.getPlayerInfo().getOpenId().equals(openId) && player.getState() == EnrollPlayerState.WAITING){
			    	returnplayer =player;
			        it.remove();
					break;
			    }
			}
		}
		
		return returnplayer;
	}
	
	/*
	 * 创建报名玩家信息，将玩家信息加入玩家列表
	 */
	private EnrollPlayerInfo createEnrollPlayerInfo(String songId, int machineId, int isChallenger, String openId) throws BeansException, SQLException{
		BasicPlayerInfo playerInfo = pm.getBasicPlayerInfo(openId);
		EnrollPlayerInfo enrollPlayerInfo = new EnrollPlayerInfo();
		enrollPlayerInfo.setIsChallenger(isChallenger);
		enrollPlayerInfo.setMachineId(machineId);
		enrollPlayerInfo.setPlayerInfo(playerInfo);
		enrollPlayerInfo.setSongId(songId);
		enrollPlayerInfo.setState(EnrollPlayerState.WAITING);
		ConnectionInfo conn = ccm.getConnectionInfo(machineId);
		enrollPlayerInfo.setPrivateIp(conn.getPrivateIp());
		enrollPlayerInfo.setPrivatePort(conn.getPrivatePort());
		enrollPlayerInfo.setPublicIp(conn.getPublicIp());
		enrollPlayerInfo.setPublicPort(conn.getPublicPort());
		return enrollPlayerInfo;
	}
	
	public int handleGetRandomEnrollList(int cmd, IRequest request) throws IllegalArgumentException, IllegalAccessException, JsonProcessingException{
		GetRandomEnrollListRequest req = (GetRandomEnrollListRequest) request;
		//System.out.println("handleGetRandomEnrollList---- getMachineId"+ req.getMachineId());
		GetRandomMEnrollListResponse rsp =new GetRandomMEnrollListResponse();
		int getNum = req.getGetNum();
		List<EnrollPlayerInfo> rdEnrollPlayerInfor=new ArrayList<EnrollPlayerInfo>() ;
		if(enrollList.isEmpty()) {
			rsp.setResult(ResultTypeList.SUCCESS);
			rsp.setPlayerInfoList(rdEnrollPlayerInfor);
			rsp.setPlayerInfoCount(rdEnrollPlayerInfor.size());
			ccm.sendResponse(PROTOID.GET_RANDOM_ENROLL_LIST_RSP, req.getMachineId(), rsp);
			return ResultTypeList.SUCCESS;
		}else {
			rdEnrollPlayerInfor = getRandomEnrollPlayer(getNum,ccm.getOpenIdByMachine(req.getMachineId()));
		}
		//System.out.println("handleGetRandomEnrollList --- NO == getNum");
		rsp.setPlayerInfoCount(rdEnrollPlayerInfor.size());
		rsp.setPlayerInfoList(rdEnrollPlayerInfor);
		rsp.setResult(ResultTypeList.SUCCESS);
		ccm.sendResponse(PROTOID.GET_RANDOM_ENROLL_LIST_RSP, req.getMachineId(), rsp);
		return ResultTypeList.SUCCESS;
	}
	
	public int handleChallengePlayer(int cmd, IRequest request) throws IllegalArgumentException, IllegalAccessException, JsonProcessingException, BeansException, SQLException {
		ChallengePlayerRequest req = (ChallengePlayerRequest) request;
		ChallengePlayerResponse rsp = new ChallengePlayerResponse();
		String chOpenId = req.getChallengerOpenId();
		String beChOpenId = req.getBeChallengerOpenId();
		String songId=req.getSongId();
		if("".equals(chOpenId)||"".equals(beChOpenId)||"".equals(songId)) {
			rsp.setResult(ResultTypeList.NULL_ARGS);
			ccm.sendResponse(PROTOID.CHALLENGE_PLAYER_RSP, req.getMachineId(), rsp);
			return ResultTypeList.NULL_ARGS;
		}		
		int beChState = pm.getGeneralPlayerInfo(beChOpenId).getState();
		if(beChState != GeneralPlayerState.ENROLLED ){
			//System.out.println("THE beChPlayState IS CANT BE CHALLENGER");
			logger.info("THE beChPlayState IS CANT BE CHALLENGER");
			rsp.setResult(ResultTypeList.OPPONENT_CANT_CHANLLENG);
			
			ccm.sendResponse(PROTOID.CHALLENGE_PLAYER_RSP, req.getMachineId(), rsp);
			return ResultTypeList.OPPONENT_CANT_CHANLLENG;
		}
		EnrollPlayerInfo beChPlayer = getMatchedPlayerByOpenId(beChOpenId,req.getSongId());//获取被挑战者
		if(beChPlayer == null){
			//System.out.println("no found beChPlayer----"+ beChOpenId);
			rsp.setResult(ResultTypeList.OPPONENT_LOST);
			ccm.sendResponse(PROTOID.CHALLENGE_PLAYER_RSP, req.getMachineId(), rsp);
			return ResultTypeList.OPPONENT_LOST;
		}
			
		//System.out.println("found a beChPlayer");
		EnrollPlayerInfo chPlayer = createEnrollPlayerInfo(req.getSongId(), req.getMachineId(), IS_CHALLENGER, chOpenId);//1表示挑战者
		List<EnrollPlayerInfo> matchPlayerList = new ArrayList<EnrollPlayerInfo>();
		matchPlayerList.add(chPlayer);
		matchPlayerList.add(beChPlayer);
		//创建一场比赛,将其加入比赛列表
		mm.createMatch(req.getSongId(), matchPlayerList);
		rsp.setResult(ResultTypeList.SUCCESS);
							
		ccm.sendResponse(PROTOID.CHALLENGE_PLAYER_RSP, req.getMachineId(), rsp);
		return ResultTypeList.SUCCESS;
			
		}
	//随机获取匹配选手
	public ArrayList<EnrollPlayerInfo> getRandomEnrollPlayer(int num, String openId) {
		
		ArrayList<EnrollPlayerInfo> randomEnrollPlayer = new ArrayList<EnrollPlayerInfo>();
		ArrayList<EnrollPlayerInfo> eList = new ArrayList<EnrollPlayerInfo>();
		int lenth=enrollList.size();
		if(lenth<1) {
			return randomEnrollPlayer;
		}
		for(List<EnrollPlayerInfo> elistSong:enrollList.values()) {
			for(EnrollPlayerInfo eInfo:elistSong) {
				if(!openId.equals(eInfo.getPlayerInfo().getOpenId()))
					eList.add(eInfo);
			}
		}
		int elistLenth =eList.size();
		if(elistLenth<=num) {//如果匹配列表里面的匹配选手小于等于num，则全部返回
			return eList;
		}
		int count =0;//记录选了多少个
		int[] checked = new int[num];//记录被选中的
		int randoms;
		int repeat=0;//记录该选取后面预留下的第几个选手		
		while(count<num) {
			randoms =random.nextInt(elistLenth-num);//留下列表后面的num个
			for(int x =0;x<count;x++) {
				if(checked[x]==randoms) {//如果这个已经选手被选中过，指定挑选后面没有参选随机的第repeat个
					randoms = elistLenth -num +repeat;
					repeat++;
					break;
				}
			}
			checked[count] =randoms;
			randomEnrollPlayer.add(eList.get(randoms));
			count++;		
		}
		return randomEnrollPlayer;
		
	}
	public EnrollPlayerInfo getEnrollPlayerInfoByOpenId(String openId) {
		EnrollPlayerInfo player = null;
		EnrollPlayerInfo returnplayer = null;
		for(String key:enrollList.keySet()) {
			List<EnrollPlayerInfo> list = getList(key);
			if(list != null){
				Iterator<EnrollPlayerInfo> it = list.iterator();
				while(it.hasNext()){
					player = it.next();
					//System.out.println("palyer: "+ player);
					if(player.getPlayerInfo().getOpenId().equals(openId)){
						returnplayer =player;
						//System.out.println("EnrollPlayerInfo - state: "+ player.getState());
						return returnplayer;
					}
				}
			}
		}
		
		return returnplayer;
	}
}
